JBoss Community Archive (Read Only)

GateIn Portal 3.7

Authentication and Authorization intro

Authentication in GateIn Portal is based on JAAS and by default it is standard J2EE FORM based authentication. However, the authentication workflow is not so easy and straightforward, because GateIn Portal supports many different authentication usecases, so that you can leverage authentication process according to your needs.

In GateIn Portal the following types of authentication are supported:

First you can see in JBOSS_HOME/gatein/gatein.ear/portal.war/WEB-INF/web.xml that authentication can be triggered by accessing secured URL /dologin :

<security-constraint>
  <web-resource-collection>
    <web-resource-name>user authentication</web-resource-name>
      <url-pattern>/dologin</url-pattern>
      <http-method>POST</http-method>
      <http-method>GET</http-method>
    </web-resource-collection>
    <auth-constraint>
      <role-name>users</role-name>
    </auth-constraint>
    <user-data-constraint>
      <transport-guarantee>NONE</transport-guarantee>
    </user-data-constraint>
  </web-resource-collection>
</security-constraint>

This means that access to URL like http://localhost:8080/portal/dologin will directly trigger the J2EE authentication in case the user is not logged. Access to this URL also means that user needs to be in JAAS group users, otherwise he can authenticate but he will have HTTP error like 403 Forbidden.

In next part of the file, you can see that authentication is FORM based and it starts by redirection to /login URL, which is actually mapped to LoginServlet.

<login-config>
  <auth-method>FORM</auth-method>
  <realm-name>gatein-domain</realm-name>
  <form-login-config>
    <form-login-page>/login</form-login-page>
    <form-error-page>/login</form-error-page>
  </form-login-config>
</login-config>

LoginServlet simply redirects user to login page placed in gatein/gatein.ear/portal.war/login/jsp/login.jsp.
images/author/download/attachments/76153514/loginScreen.png

So if you want to change somehow the look and feel of this login page, you can do it in this JSP file. Alternatively you can create extension and override this page via extension if you don't want to edit it directly. You can also change/override image or CSS placed in gatein/gatein.ear/login/skin.

After the user has submitted his login form, he will be redirected to login URL, which looks like http://localhost:8080/portal/login?username=root&password=gtn&initialURI=/portal/classic, which is again mapped to LoginServlet. Now LoginServlet will trigger WCI login, which delegates to Servlet API (method HttpServletRequest.login(String username, String password) available in Servlet 3.0) and additionaly it triggers WCI Authentication listeners. Login through Servlet API will delegate to JAAS.

Login modules

So from WCI servlet API login, you are redirected to the JAAS authentication. GateIn Portal is using its own security domain gatein-domain with a set of predefined login modules. Login module configuration for gatein-domain is in the JBOSS_HOME/standalone/configuration/standalone.xml in JBoss AS7 and in TOMCAT_HOME/conf/jaas.conf in Tomcat 7. By default you can see this login modules stack:

<security-domain name="gatein-domain" cache-type="default">
  <authentication>
    <login-module code="org.gatein.sso.integration.SSODelegateLoginModule" flag="required">
      <module-option name="enabled" value="#{gatein.sso.login.module.enabled}" />
      <module-option name="delegateClassName" value="#{gatein.sso.login.module.class}" />
      <module-option name="portalContainerName" value="portal" />
      <module-option name="realmName" value="gatein-domain" />
      <module-option name="password-stacking" value="useFirstPass" />
    </login-module>
    <login-module code="org.exoplatform.services.security.j2ee.JBossAS7LoginModule" flag="required">
      <module-option name="portalContainerName" value="portal"/>
      <module-option name="realmName" value="gatein-domain"/>
    </login-module>
  </authentication>
</security-domain>

You are free to add some new login modules or completely replace existing login modules with some of your own.

Authentication starts with invoke of the login method on each login module. After all login methods are invoked, the authentication is continued by invoke of the commit method on each login module. Both login and commit methods can throw LoginException. If it happens, then the whole authentication ends unsuccessfully, which in next turn invokes the abort method on each login module. By returning "false" from method login, you can ensure that login module is ignored. This is not specific to GateIn Portal but it is generic to JAAS. See here for more information about login modules in general.

Existing login modules

Here is some brief description of existing login modules:

  • OAuthLoginModule - It's useful only if OAuth authentication is enabled (disabled by default. See OAuth - Authentication with social network accounts with instructions on how to enable it.
    If OAuth is used and OAuth authentication succeed, the special Identity object will be created and saved into shared state map (Map, which is shared between all login modules), so that this Identity object can be used by JBossAS7LoginModule or other login modules in the JAAS chain.

  • SSODelegateLoginModule - It's useful only if SSO authentication is enabled (disabled by default. It can be enabled through properties in configuration.properties file and in this case it delegates the work to another real login module for SSO integration. If SSO is disabled, SSODelegateLoginModule is simply ignored. See Central Authentication Service (CAS)#Configuration properties details for more details.
    If SSO is used and SSO authentication succeed, the special Identity object will be created and saved into shared state map (Map, which is shared between all login modules), so that this Identity object can be used by JBossAS7LoginModule or other login modules in the JAAS chain.

  • JBossAS7LoginModule - Most important login module, which is normally used to perform whole authentication by itself. First it checks if Identity object has been already created and saved into sharedState map by previous login modules (like SSODelegateLoginModule, CustomMembershipLoginModule or SharedStateLoginModule). If not, it triggers real authentication of user with usage of Authenticator interface and it will use Authentication.validateUser(Credential[] credentials) which performs real authentication of username and password against OrganizationService and portal identity database. See Authenticator and RolesExtractor for details about Authenticator and about Identity objects.
    In the JbossAS7LoginModule.commit method, the Identity object is registered to IdentityRegistry, which will be used later for authorization. Also some JAAS principals (UserPrincipal and RolesPrincipal) and assigned to our authenticated Subject. This is needed for JBoss AS server, so that it can properly recognize name of logged user and his roles on JBoss AS level.

There is couple of other login modules, which are not active by default, but you can add them if you find them useful.

  • CustomMembershipLoginModule- special login module, which can be used to add user to some existing groups during the successful login of this user. The group name is configurable and by default is /platform/users group. The login module is not used because in normal environment, users are already in the /platform/users group. It is useful only for some special setups like read-only LDAP, where groups of ldap user are taken from ldap tree so that users may not be in the /platform/users group, which is needed for successful authorization.
    Note that CustomMembershipLoginModule can't be first login module in LM chain because it assumes that Identity object is already available in shared state. So there are those possible cases:

    • For non-SSO case, you may need to chain this LM with other login modules, which can be used to establish Identity and add it into shared state. Those LM can be InitSharedStateLoginModule and SharedStateLoginModule. See below.

    • For SSO (or OAuth) case, you can simply add CustomMembershipLoginModule between SSODelegateLoginModule and JBossAS7LoginModule.

Configuration example with CustomMembershipLoginModule and disabled SSO:

<login-module code="org.exoplatform.web.security.InitSharedStateLoginModule" flag="required">
  <module-option name="portalContainerName" value="portal"/>
  <module-option name="realmName" value="gatein-domain"/>
</login-module>
<login-module code="org.exoplatform.services.security.jaas.SharedStateLoginModule" flag="required">
  <module-option name="portalContainerName" value="portal"/>
  <module-option name="realmName" value="gatein-domain"/>
</login-module>
<login-module code="org.exoplatform.services.organization.idm.CustomMembershipLoginModule" flag="required">
  <module-option name="portalContainerName" value="portal"/>
  <module-option name="realmName" value="gatein-domain"/>
  <module-option name="membershipType" value="member" />
  <module-option name="groupId" value="/platform/users" />
</login-module>
<login-module code="org.exoplatform.services.security.j2ee.JBossAS7LoginModule" flag="required">
  <module-option name="portalContainerName" value="portal"/>
  <module-option name="realmName" value="gatein-domain"/>
</login-module>

And now configuration example with enabled SSO or OAuth:

<login-module code="org.gatein.security.oauth.jaas.OAuthLoginModule" flag="required">
  <module-option name="portalContainerName" value="portal"/>
  <module-option name="realmName" value="gatein-domain"/>
</login-module>
<login-module code="org.gatein.sso.integration.SSODelegateLoginModule" flag="required">
  <module-option name="enabled" value="#{gatein.sso.login.module.enabled}" />
  <module-option name="delegateClassName" value="#{gatein.sso.login.module.class}" />
  <module-option name="portalContainerName" value="portal" />
  <module-option name="realmName" value="gatein-domain" />
  <module-option name="password-stacking" value="useFirstPass" />
</login-module>
<login-module code="org.exoplatform.services.organization.idm.CustomMembershipLoginModule" flag="required">
  <module-option name="portalContainerName" value="portal"/>
  <module-option name="realmName" value="gatein-domain"/>
  <module-option name="membershipType" value="member" />
  <module-option name="groupId" value="/platform/users" />
</login-module>
<login-module code="org.exoplatform.services.security.j2ee.JBossAS7LoginModule" flag="required">
  <module-option name="portalContainerName" value="portal"/>
  <module-option name="realmName" value="gatein-domain"/>
</login-module>
  • InitSharedStateLoginModule - It can read credentials from JAAS callback and add them into shared state. It's intended to be used in JAAS chain before SharedStateLoginModule

  • SharedStateLoginModule- It reads username and password from sharedState map from attributes javax.security.auth.login.name and javax.security.auth.login.password. Then it calls Authenticator.validateUser(Credential[] credentials), to perform authentication of username and password against OrganizationService and portal identity database. Result of successful authentication is object Identity, which is saved to sharedState map .

Creating your own login module

Before creating your own login module, it is recommended you study source code of existing login modules to better understand the whole JAAS authentication process. You need to have good knowledge so that you can properly decide where your login module should be placed and if you need to replace some existing login modules or simply attach your own module to existing chain.

There are actually two levels of authentication and the overall result of JAAS authentication should properly handle both these cases:

  • Authentication at application server level.

  • Authentication at GateIn Portal level.

Authentication at application server level

Application server needs to properly recognize that user is successfully logged and it has assigned his JAAS roles. Unfortunately this part is not standardized and is specific for each AS. For example in JBoss AS, you need to ensure that JAAS Subject has assigned principal with username (UserPrincipal) and also RolesPrincipal, which has name "Roles" and it contains list of JAAS roles. This part is actually done in JbossLoginModule.commit(). In Tomcat, this flow is little different, which means Tomcat has it is own TomcatLoginModule.

After successful authentication, the user needs to be at least in JAAS role users because this role is declared in web.xml as you saw above. The JAAS roles are extracted by the special algorithm from GateIn Portal memberships. See below in section with RolesExtractor.

Authentication at portal level

Login process needs to create special object org.exoplatform.services.security.Identity and register this object into IdentityRegistry component of GateIn Portal. This Identity object should encapsulate username of authenticated user, memberships of this user and JAAS roles. Identity object can be easily created with the Authenticator interface as shown below.

Authenticator and RolesExtractor

Authenticator is an important component in the authentication process. Actually interface org.exoplatform.services.security.Authenticator looks like this:

public interface Authenticator
{

   /**
    * Authenticate user and return userId.
    *
    * @param credentials - list of users credentials (such as name/password, X509
    *          certificate etc)
    * @return userId
    */
   String validateUser(Credential[] credentials) throws LoginException, Exception;

   /**
    * @param userId.
    * @return Identity
    */
   Identity createIdentity(String userId) throws Exception;

}

The validateUser method is used to check whether given credentials (username and password) are really valid. So it performs real authentication. It returns the username if credentials are correct. Otherwise, LoginException is thrown.

The createIdentity method is used to create instance of the org.exoplatform.services.security.Identity object, which encapsulates all important information about single user like:

  • Username.

  • Set of Memberships (MembershipEntry objects) which the user belongs to. Membership is object, which contains information about membershipType (manager, member, validator, and more) and about group (/platform/users, /platform/administrators, /partners, /organization/management/executiveBoard, and more).

  • Set of Strings with JAAS roles of given user. JAAS roles are simple Strings, which are mapped from MembershipEntry objects. There is another special component org.exoplatform.services.security.RolesExtractor, which is used to map JAAS roles from MembershipEntry objects. RolesExtractor interface looks like this:

    public interface RolesExtractor
    {
    
       /**
        * Extracts J2EE roles from userId and|or groups the user belongs to both
        * parameters may be null
        *
        * @param userId
        * @param memberships
        */
       Set<String> extractRoles(String userId, Set<MembershipEntry> memberships);
    }

    The default implementation named DefaultRolesExtractorImpl is based on special algorithm, which uses name of role from the root of the group (for example you have JAAS role "organization" for the "/organization/management/something" role). The only exception is "platform" group where the second level is used as the group name. For example, from "/platform/users" group, you have the JAAS role "users".

Assuming that you have user root, which has memberships member:/platform/users, manager:/platform/administrators, validator:/platform/managers, member:/partners, member:/customers/acme, member:/organization/management/board. In this case, you will have JAAS roles: users, administrators, managers, partners, customers, organization.

Default implementation of Authenticator is OrganizationAuthenticatorImpl, which is implementation based on OrganizationService. See Organization API .

You can override the default implementation of mentioned interfaces (Authenticator and RolesExtractor) if the default behavior is not suitable for your needs.

Different authentication workflows

RememberMe authentication

In default login dialog, you can notice that there is "Remember my login" checkbox, which users can use to persist their login on his workstation. Default validity period of RememberMe cookie is 1 day (it is configurable), and so user can be logged for whole day before he needs to reauthenticate again with his credentials.

How does it work

  • The user checks the "Remember my login" checkbox on login screen of GateIn Portal, then submits the form.

  • HTTP request, such as http://localhost:8080/portal/login?initialURI=/portal/classic&username=root&password=gtn&rememberme=true, is sent to server.

  • Request is processed by the LoginController servlet. The servlet obtains instance of RemindPasswordTokenService and save user credentials into JCR. It generates and returns special token (key) for later use. Then it creates cookie called rememberme and use returned token as value of cookie.

Reauthentication

  • After some time, user wants to reauthenticate. It is assumed that his HTTP Session is already expired but his RememberMe cookie is still active.

  • The user sends the HTTP request to some portal pages (for example, http://localhost:8080/portal/classic).

  • There is special HTTP filter named RememberMeFilter configured in web.xml, which checks rememberme cookie and then it retrieves credentials of user from RemindPasswordTokenService. Now the filter redirects request to PortalLoginController and authentication process goes in same way as for normal FORM based authentication.

RemindPasswordTokenService

This is a special service used during the RememberMe authentication workflow. It is configurable in the GATEIN_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/common/remindpwd-configuration.xml file. See Authentication Token Configuration for more details.

Another thing is that you can encrypt passwords before store them into JCR. More info is in section Password Encryption.

Authorization

In the previous section, you have learned about JAAS authentication and about login modules. So you know that result of authentication, including:

  • JAAS Subject with principals for username (UserPrincipal) and for JAAS roles (RolesPrincipal).

  • Identity object, which encapsulates username, all memberships and all JAAS roles. This Identity object is bound to IdentityRegistry component.
    Authorization in GateIn Portal actually happens on two levels:

Servlet container authorization

First round of authorization is servlet container authorization based on secured URL from web.xml. You can see above in the web.xml snippet that secured URL are accessible only for users from the users role:

<auth-constraint>
  <role-name>users</role-name>
</auth-constraint>

This actually means that your user needs to be in GateIn Portal role /platform/users (See Authenticator and RolesExtractor for details). In other words, if the authentication is successful but your user is not in the /platform/users group, it means that he is not in JAAS role users, which in next turn means that he will have authorization error named 403 Forbidden thrown by the servlet container. For example in LDAP setup, your users may not be in /platform/users by default, but you can use CustomMembershipLoginModule to fix this problem. For details see Login modules

You can change the behaviour and possibly add some more auth-constraint elements into web.xml. However, this protection of resources based on web.xml is not standard GateIn Portal way and it is mentioned here mainly for illustration purposes.

Portal level authorization

Second round of authorization is based on the UserACL component (See Portal Default Permission Configuration). You can declare access and edit permissions for portals, pages and/or portlets. UserACL is then used to check if our user has particular permissions to access or edit specified resource. Important object with information about roles of our user is mentioned Identity object created during the JAAS authentication.

Authorization on portal level looks like this:

  • The user sends the HTTP request to some URLs in portal.

  • The HTTP request is processed through SetCurrentIdentityFilter, which is declared in gatein/gatein.ear/portal.war/WEB-INF/web.xml.

  • SetCurrentIdentityFilter reads username of current user from HttpServletRequest.getRemoteUser(). Then it looks for Identity of this user in IdentityRegistry, where Identity has been saved during authentication. The found Identity is then encapsulated into the ConversationState object and bound into the ThreadLocal variable.

  • UserACL is able to obtain Identity of current user from the UserACL.getIdentity() method, which simply calls ConversationState.getCurrent().getIdentity() for finding the current Identity bound to ThreadLocal. Now UserACL has identity of user so that it can perform any security checks.

JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-10 13:11:04 UTC, last content change 2013-05-31 16:25:21 UTC.